package com.baomidou.ant.controller;

import com.alibaba.fastjson.JSONObject;
import com.baomidou.ant.common.MessageTypeEnum;
import com.baomidou.ant.common.StatusEnum;
import com.baomidou.ant.common.Result;
import com.baomidou.ant.entity.*;
import com.baomidou.ant.service.*;
import com.baomidou.ant.utils.MyStringUtils;
import com.baomidou.ant.vo.Request.*;
import com.baomidou.ant.vo.Response.*;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.ObjectUtils;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import io.goeasy.GoEasy;
import jdk.nashorn.internal.objects.annotations.Getter;
import lombok.extern.log4j.Log4j;
import lombok.extern.log4j.Log4j2;
import lombok.val;
import org.omg.PortableInterceptor.USER_EXCEPTION;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.event.EventListener;
import org.springframework.messaging.handler.annotation.MessageMapping;
import org.springframework.messaging.handler.annotation.Payload;
import org.springframework.messaging.handler.annotation.SendTo;
import org.springframework.messaging.simp.SimpMessageHeaderAccessor;
import org.springframework.messaging.simp.stomp.StompHeaderAccessor;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.socket.messaging.SessionDisconnectEvent;

import java.io.IOException;
import java.util.*;
import java.util.stream.Collectors;


/**
 * <p>
 *  前端控制器
 * </p>
 *
 * @author ZHOU BB
 * @since 2021-03-24
 * 本问答游戏规则如下：
 * 用户打开页面后，输入自己的昵称，发送给服务端，服务端将用户昵称保存到hashmap，并记录用户状态（空闲，游戏中），接着用户进入大厅。
 * 大厅中用户可以互相选择，一旦某用户选择了另一位用户，将触发开始游戏，双方进入答题模式。
 * 答题的两位用户各回答10题，每题答对为10分，共100分，左上角页面显示自己的分数，右上角显示对方分数，实时通过websocket接收对方分数。
 * 10题结束，双方等待对方总分，最后判断输赢，显示结果界面。
 * 所以我们需要设计三个WebSocket协议：
 * 用户创建昵称，进入玩家大厅
 * 用户选择对手，双方进入游戏
 * 对战过程实时显示双方分数
 * ————————————————
 *
 */

@RestController
@Log4j2
@RequestMapping("/pvp-info")
public class PvpInfoController {
    @Autowired
    private IPvpInfoService pvpInfoService;
    @Autowired
    private IQuestionInfoService questionInfoService;
    @Autowired
    private IUserService userService;
    @Autowired
    private IPvpRecordService pvpRecordService;
    @Autowired
    private IPvpRoomService pvpRoomService;
    @Autowired
    private IPvpMessageService pvpMessageService;
    @Autowired
    private IErrorBookService errorBookService;

    //对战房间用户及其状态,HashMap存储用户状态
    private Map<String, StatusEnum> userToStatus = new HashMap<>();
    //用户在游戏中,sender和receiver
    private Map<String, String> userToPlay = new HashMap<>();

    //挑战者和被挑战者
    private ChooseEnemyVo vo;


    /**
     * 进入游戏
     * @param userId
     * @return
     */
    @GetMapping("/enter")
    private Result enterGame(Integer userId){
        return null;
    }


    /**
     * 进入pvp后，显示自己的信息和段位
     * @param userId
     * @return
     */
    @GetMapping("/myRecord/{userId}")
    public Result myRecord(@PathVariable Integer userId){
        MyRecordVo vo=pvpRecordService.checkMyRecord(userId);
        return Result.getSuccess().setData(vo);
    }



    /**
     * 进入pvp后,显示所有连接用户战绩
     * @return
     */
    @PostMapping("/allConnect")
    public Result allConnectUser(@RequestBody UserIdRequestVo vo){
        IPage<AllConnectUserVo> connectList=pvpRoomService.listConnectUser(vo);
        //返回
        return Result.getSuccess().setData(connectList);
    }

    /**
     * 选择被挑战玩家
     * @return
     */
    @PostMapping("/chooseEnemy")
    public Result chooseEnemy(@RequestBody ChooseEnemyVo vo){
        //判断对方连接状态
        PvpRoom enemyPvpRoom=pvpRoomService.queryByUserId(vo.getEnemyId());
        if(Objects.equals(enemyPvpRoom.getConnectStatus(), "0")){
            return Result.getFailure().setMsg("对手闻风丧胆，逃跑了");
        }
        if(Objects.equals(enemyPvpRoom.getConnectStatus(), "2")){
            return Result.getFailure().setMsg("手慢了，该玩家已经被其他人选中或选中了其他玩家");
        }
        if(Objects.equals(enemyPvpRoom.getConnectStatus(), "3")){
            return Result.getFailure().setMsg("该玩家正在游戏中...");
        }
        //如果对方空闲,则选中成功
        enemyPvpRoom.setConnectStatus("2");
        pvpRoomService.saveOrUpdate(enemyPvpRoom);
        //自己状态改变了
        PvpRoom userPvpRoom=pvpRoomService.queryByUserId(vo.getUserId());
        userPvpRoom.setConnectStatus("2");
        pvpRoomService.saveOrUpdate(userPvpRoom);
        //执行推送给被选玩家
        this.pushEnemy(vo);
        //返回提示
        return Result.getSuccess().setMsg("正在发起挑战...");
    }

    /**
     * 推送数据----向被挑战玩家推送
     */
    @PostMapping("/socket/pushEnemy")
    public Result pushEnemy(@RequestBody ChooseEnemyVo vo) {
        try {
            //定义信息
            User user=userService.getById(vo.getUserId());
            String s= String.valueOf(vo.getUserId());
            //添加pvpMessage
            PvpMessage message=new PvpMessage();
            message.setRequestUser(vo.getUserId());
            message.setResponseUser(vo.getEnemyId());
            message.setStatus("0");
            pvpMessageService.save(message);
            //向被挑战方发送信息
            WebSocketServer.sendInfo (s, String.valueOf(vo.getEnemyId()));
        } catch (Exception e) {
            e.printStackTrace();
            return Result.getFailure();
        }
        //返回双方信息
        return Result.getSuccess().setData(vo);
    }

    /**
     * 拒绝或者接受接口
     * @param vo
     * @return
     */
    @PostMapping("/myRequest")
    public Result myRequest(@RequestBody MyRequestVo vo){
        //消息设为已读
        QueryWrapper<PvpMessage> queryWrapper=new QueryWrapper<>();
        queryWrapper.eq("request_user",vo.getUserId());
        queryWrapper.eq("response_user",vo.getEnemyId());
        queryWrapper.eq("status","0");
        PvpMessage pvpMessage=pvpMessageService.getOne(queryWrapper);
        pvpMessage.setStatus("1");
        pvpMessageService.saveOrUpdate(pvpMessage);
        //判断是拒绝还是同意
        if(Objects.equals(vo.getChoose(), "y")){
            //双方进入游戏中状态
            PvpRoom userPvpRoom=pvpRoomService.queryByUserId(vo.getUserId());
            userPvpRoom.setConnectStatus("3");
            pvpRoomService.saveOrUpdate(userPvpRoom);

            PvpRoom enemyPvpRoom=pvpRoomService.queryByUserId(vo.getEnemyId());
            enemyPvpRoom.setConnectStatus("3");
            pvpRoomService.saveOrUpdate(enemyPvpRoom);
            //执行建立战绩接口
            this.createRecord(vo.getEnemyId(),vo.getUserId());
            this.pushUser("y",vo.getUserId(), vo.getEnemyId());
            //成功
            return Result.getSuccess().setMsg("进入对战房间中....");
        }
        //不同意，用户改回在线空闲状态
        PvpRoom enemyPvpRoom=pvpRoomService.queryByUserId(vo.getEnemyId());
        enemyPvpRoom.setConnectStatus("1");
        pvpRoomService.saveOrUpdate(enemyPvpRoom);
        //拒绝
        if(Objects.equals(vo.getChoose(), "n")){
            this.pushUser("n",vo.getUserId(),vo.getEnemyId());
            return Result.getFailure().setMsg("你拒绝了这次对战!");
        }
        this.pushUser("n",vo.getUserId(),vo.getEnemyId());
        return Result.getFailure();
    }

    /**
     * 被挑战者同意或者拒绝，推送消息给挑战者
     * @param enemyChoose
     * @param userId
     * @return
     */
    @PostMapping("/socket/pushUser")
    public Result pushUser(String enemyChoose,Integer userId,Integer enemyId){
        //对方同意
        if(ObjectUtils.isNotEmpty(enemyChoose)&&enemyChoose.equals("y")){
            //向挑战方发送信息
            try {
                Map<String,Integer> map=new HashMap<>();
                map.put("挑战者ID",userId);
                map.put("enemyID",enemyId);
                String s=JSONObject.toJSONString(map);
                WebSocketServer.sendInfo (s, String.valueOf(userId));
            } catch (IOException e) {
                e.printStackTrace();
            }
            return Result.getSuccess();
        }
        //对方拒绝
        else{
            try {
                String  s = "对方拒绝了你的挑战....";
                WebSocketServer.sendInfo (s, String.valueOf(userId));
            } catch (IOException e) {
                e.printStackTrace();
            }
            return Result.getFailure();
        }
    }


    /**
     *  对战用户建立双方战绩数据方法
     *  为双方用户建立一条状态为1的战绩数据
     * @param userId
     * @param enemyId
     */
    public void createRecord(Integer userId,Integer enemyId){
        PvpInfo pvpInfo=new PvpInfo();
        pvpInfo.setUserId(userId);
        pvpInfo.setEnemy(enemyId);
        pvpInfo.setStatus("1");
        //随机生成题目
        List<QuestionInfo> questionList=questionInfoService.list();
        int max=questionList.size()-1;
        int min=2;
        Random random=new Random();
        //定义返回值
        String ids= String.valueOf(random.nextInt(max)%(max-min+1)+ min);
        //随机生成5道题
        for(int i=0;i<=max;i++){
            int result=random.nextInt(max)%(max-min+1)+ min;
            //判断是否随机到重复数
            if(!ids.contains(String.valueOf(result))){
                ids=MyStringUtils.toString(result,ids);
            }
            if(ids.length()-ids.replace(",","").length()>=4){
                break;
            }
        }
        pvpInfo.setQuestionId(ids);
        pvpInfo.setUserName(userService.getById(userId).getUsername());
        pvpInfo.setEnemyName(userService.getById(enemyId).getUsername());
        pvpInfo.setIds(userId+","+enemyId);

        pvpInfoService.save(pvpInfo);
    }

    /**
     * 进入界面就调用接口
     * 双方就绪进入对战房间,互相推送对方信息
     * @param id
     */
    @GetMapping("/socket/enterRoom/{id}")
    public void pvpRoom(@PathVariable Integer id){
        QueryWrapper<PvpMessage> queryWrapper=new QueryWrapper<>();
        queryWrapper.eq("request_user",id).or().eq("response_user",id);
        queryWrapper.eq("status","1");
        //可能存在的隐患
        PvpMessage pvpMessage=pvpMessageService.getOne(queryWrapper);
        //定义map
        Map<String,Object> map=new HashMap<>();
        //得到被挑战者信息
        User responseUser=userService.getById(pvpMessage.getResponseUser());
        UserPvpVo responseVo=new UserPvpVo();
        responseVo.setUserName(responseUser.getUsername());
        responseVo.setUserImage(responseUser.getUserImage());
        //得到挑战者信息
        User requestUser=userService.getById(pvpMessage.getRequestUser());
        UserPvpVo requestVo = new UserPvpVo();
        requestVo.setUserName(requestUser.getUsername());
        requestVo.setUserImage(requestUser.getUserImage());
        //转化为json字符串
        map.put("requestUser",requestUser);
        map.put("responseUser",responseUser);
        String s = JSONObject.toJSONString(map);
        //执行推送消息
        this.pushSome(String.valueOf(id),s);
    }


    /**
     * 进入界面就调用接口
     * 题目推送
     */
    @GetMapping("/socket/pushQuestion/{id}")
    public void pushQuestion(@PathVariable Integer id){
        //找到自己的题目
        QueryWrapper<PvpInfo> queryWrapper=new QueryWrapper<>();
        queryWrapper.eq("user_id",id).or().eq("enemy",id);
        queryWrapper.eq("status",1);
        List<PvpInfo>  pvpInfoList=pvpInfoService.list(queryWrapper);
        PvpInfo pvpInfo=pvpInfoList.get(pvpInfoList.size()-1);
        //得到题目id
        String questionId= pvpInfo.getQuestionId();
        String[] ids=questionId.split(",");
        //返回题目id
        ResponseQuestionIdVo vo=new ResponseQuestionIdVo();
        vo.setId1(Integer.valueOf(ids[0]));
        vo.setId2(Integer.valueOf(ids[1]));
        vo.setId3(Integer.valueOf(ids[2]));
        vo.setId4(Integer.valueOf(ids[3]));
        vo.setId5(Integer.valueOf(ids[4]));
        String s=JSONObject.toJSONString(vo);
        String id1=pvpInfo.getUserId()+","+pvpInfo.getEnemy();
        String id2=pvpInfo.getEnemy()+","+pvpInfo.getUserId();
        //推送题目
        this.pushSome(id1,s);
        this.pushSome(id2,s);
    }

    /**
     * 判断正误
     * 每次答完就要推送一次消息
     */
    @PostMapping("/trueOrFalse")
    public Result isTrue(@RequestBody PvpChoiceInfoVo vo){
        QueryWrapper<PvpInfo> queryWrapper=new QueryWrapper<>();
        queryWrapper.eq("status","1");
        String ids1=vo.getUserId()+","+vo.getEnemyId();
        String ids2=vo.getEnemyId()+","+vo.getUserId();
        queryWrapper.eq("ids",ids1).or().eq("ids",ids2);
        //得到正在进行的对战
        List<PvpInfo> pvpInfoList=pvpInfoService.list(queryWrapper);
        PvpInfo pvpInfo=pvpInfoList.get(pvpInfoList.size()-1);
        //判断是挑战者还是被挑战者
        String user=null;
        if(vo.getUserId().equals(pvpInfo.getUserId())){
            user="U";
        }else
            user="E";
        //如果答对了
        if(pvpInfoService.trueOrFalse(vo)){
            //执行加分,要判断是挑战者还是被挑战者,准确加分
            if(Objects.equals(user, "U")){
                pvpInfo.setMypoints(pvpInfo.getMypoints()+20);
                pvpInfoService.saveOrUpdate(pvpInfo);
            }else {
                pvpInfo.setHispoints(pvpInfo.getHispoints()+20);
                pvpInfoService.saveOrUpdate(pvpInfo);
            }
            //推送消息
            Map<String,Integer> map=new HashMap<>();
            //如果答题者为挑战者
            if(user.equals("U")){
                map.put("userId",pvpInfo.getUserId());
                map.put("point",pvpInfo.getMypoints());
                map.put("分数",pvpInfo.getMypoints());
                //答了一题
                pvpInfo.setQuestions(MyStringUtils.addNum(pvpInfo.getQuestions(),0));
            }else{
                map.put("userId",pvpInfo.getEnemy());
                map.put("point",pvpInfo.getHispoints());
                map.put("分数",pvpInfo.getHispoints());
                pvpInfo.setQuestions(MyStringUtils.addNum(pvpInfo.getQuestions(),1));
            }
            //定义返回信息
            String s=JSONObject.toJSONString(map);
            //拼接返回地址
            String id1=vo.getUserId()+","+vo.getEnemyId();
            String id2=vo.getEnemyId()+","+vo.getUserId();
            //执行发送
            this.pushSome(id1,s);
            this.pushSome(id2,s);
            pvpInfoService.saveOrUpdate(pvpInfo);
            return Result.getSuccess().setMsg("答对了");
        }else{
            //纪录错题本
            QueryWrapper<ErrorBook> queryWrapper1=new QueryWrapper<>();
            queryWrapper1.eq("user_id",vo.getUserId());
            ErrorBook errorBook=errorBookService.getOne(queryWrapper1);
            int[] errorE= MyStringUtils.toInt(errorBook.getEasyPvp());
            int[] errorN= MyStringUtils.toInt(errorBook.getNormalPvp());
            int[] errorH= MyStringUtils.toInt(errorBook.getHardPvp());
            List<Integer> errorEasy = Arrays.stream(errorE).boxed().collect(Collectors.toList());
            List<Integer> errorNormal= Arrays.stream(errorN).boxed().collect(Collectors.toList());
            List<Integer> errorHard= Arrays.stream(errorH).boxed().collect(Collectors.toList());
            //判断题的难度,并判断是否纪录过该数据
            QuestionInfo questionInfo=questionInfoService.getById(vo.getQuestionId());
            if (Objects.equals(questionInfo.getQuestionLevel(), "简单")) {
                if(!errorEasy.contains(questionInfo.getId())){
                    errorBook.setEasyPvp(MyStringUtils.toString(vo.getQuestionId(),errorBook.getEasyPvp()));
                }
            }else if(Objects.equals(questionInfo.getQuestionLevel(), "中等")){
                if(!errorNormal.contains(questionInfo.getId())){
                    errorBook.setNormalPvp(MyStringUtils.toString(vo.getQuestionId(),errorBook.getNormalPvp()));
                }
            }else{
                if(!errorHard.contains(questionInfo.getId())){
                    errorBook.setHardPvp(MyStringUtils.toString(vo.getQuestionId(),errorBook.getHardPvp()));
                }
            }
            //保存错题本
            errorBookService.saveOrUpdate(errorBook);
            //推送
            Map<String,Integer> map=new HashMap<>();
            //如果答题者为挑战者
            if(user.equals("U")){
                map.put("userId",pvpInfo.getUserId());
                map.put("points",pvpInfo.getMypoints());
                map.put("分数",pvpInfo.getMypoints());
                pvpInfo.setQuestions(MyStringUtils.addNum(pvpInfo.getQuestions(),0));
            }else{
                map.put("userId",pvpInfo.getEnemy());
                map.put("points",pvpInfo.getHispoints());
                map.put("分数",pvpInfo.getHispoints());
                pvpInfo.setQuestions(MyStringUtils.addNum(pvpInfo.getQuestions(),1));
            }
            //定义返回信息
            String s=JSONObject.toJSONString(map);
            //拼接返回地址
            String id1=vo.getUserId()+","+vo.getEnemyId();
            String id2=vo.getEnemyId()+","+vo.getUserId();
            //执行发送
            this.pushSome(id1,s);
            this.pushSome(id2,s);
            pvpInfoService.saveOrUpdate(pvpInfo);
            return Result.getSuccess().setMsg("答错了");
        }
    }


    /**
     * 根据用户，推送消息到对战房间
     * @param userId
     */
    @GetMapping("/socket/pushSome/{userId}")
    public Result pushSome(@PathVariable String userId,String message){
        try {
            String ids=null;
            //判断webSocketSet是否有特定的code
            for (WebSocketServer item : WebSocketServer.webSocketSet ) {
                //得到包含该用户的item，且是游戏中的时候
                if(item.userId.contains(userId)&&item.userId.contains(",")){
                    ids=item.userId;
                }
            }
            //如果没有包含的code的设备，不发
            if (ids==null){
                return Result.getFailure();
            }
            //发送信息到对战房间
            WebSocketServer.sendInfo(message,ids);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return Result.getSuccess();
    }







    //用户查询对战纪录
    @PostMapping("/listOfUser")
    private Result listPvpInfo(@RequestBody RequestPvpListOfUserVo requestPvpListOfUserVo){
        IPage<ResponsePvpListOfUserVo> iPage=pvpInfoService.listOfUser(requestPvpListOfUserVo);
        return Result.getSuccess().setData(iPage);
    }


    //是否能结束对战
    @GetMapping("/isOver/{ids}")
    public Result isOver(@PathVariable String ids){
        //找到特定的对战纪录
        List<Integer> list=MyStringUtils.getNum(ids);
        String ids1=list.get(0)+","+ list.get(1);
        String ids2=list.get(1)+","+ list.get(0);
        QueryWrapper<PvpInfo> queryWrapper=new QueryWrapper<>();
        queryWrapper.eq("status","1");
        queryWrapper.eq("ids",ids1).or().eq("ids",ids2);
        List<PvpInfo> pvpInfoList=pvpInfoService.list(queryWrapper);
        PvpInfo pvpInfo=pvpInfoList.get(pvpInfoList.size()-1);
        //判断双方是否都答完
        List<Integer> result=MyStringUtils.getNum(pvpInfo.getQuestions());
        if(Objects.equals(result.get(0), result.get(1))){
            return Result.getSuccess();
        }
        return Result.getFailure();
    }

    //结束对战
    @PostMapping("/over")
    public Result over(@RequestBody RequestOverGameVo requestOverGameVo){
        String ids1=requestOverGameVo.getUserId()+","+requestOverGameVo.getEnemy();
        String ids2=requestOverGameVo.getEnemy()+","+requestOverGameVo.getUserId();
        //找到特定的对战记录
        QueryWrapper<PvpInfo> queryWrapper=new QueryWrapper<>();
        queryWrapper.eq("status","1");
        queryWrapper.eq("ids",ids1).or().eq("ids",ids2);
        List<PvpInfo> pvpInfoList=pvpInfoService.list(queryWrapper);
        //有对战记录则执行，防止双方各调用一次
        if(pvpInfoList!=null&&pvpInfoList.size()!=0){
            PvpInfo pvpInfo = pvpInfoList.get(pvpInfoList.size() - 1);
            //结束了
            pvpInfo.setStatus("0");
            //找到双方战绩
            PvpRecord userRecord=pvpRecordService.getByUserId(requestOverGameVo.getUserId());
            PvpRecord enemyRecord=pvpRecordService.getByUserId(requestOverGameVo.getEnemy());
            //判断双方分数，赋予挑战结果
            if(requestOverGameVo.getMypoints()>requestOverGameVo.getHispoints()){
                //判断你是挑战者还是被挑战者,因为对战结果是挑战者的对战结果，如果被挑战者赢了，就应该赋予失败
                if(requestOverGameVo.getUserId()==pvpInfo.getUserId()){//你是挑战者
                    pvpInfo.setResult("胜");
                } else if(requestOverGameVo.getUserId()==pvpInfo.getEnemy()) {//你是被挑战者
                    pvpInfo.setResult("败");
                }
                userRecord.setWinning(userRecord.getWinning()+20);
                enemyRecord.setWinning(enemyRecord.getWinning()-15);
                pvpRecordService.saveOrUpdate(userRecord);
                pvpRecordService.saveOrUpdate(enemyRecord);
                pvpInfoService.saveOrUpdate(pvpInfo);
                return Result.getSuccess().setMsg("对战结束,你胜利了");
            }
            if(requestOverGameVo.getMypoints()<requestOverGameVo.getHispoints()){
                if(requestOverGameVo.getUserId()==pvpInfo.getUserId()){//你是挑战者
                    pvpInfo.setResult("败");
                } else if(requestOverGameVo.getUserId()==pvpInfo.getEnemy())
                {//你是被挑战者
                    pvpInfo.setResult("胜");
                }
                userRecord.setWinning(userRecord.getWinning()-15);
                enemyRecord.setWinning(enemyRecord.getWinning()+20);
                pvpRecordService.saveOrUpdate(userRecord);
                pvpRecordService.saveOrUpdate(enemyRecord);
                pvpInfoService.saveOrUpdate(pvpInfo);
                return Result.getSuccess().setMsg("对战结束,你失败了");
            }
            if(requestOverGameVo.getHispoints()==requestOverGameVo.getMypoints()){
                pvpInfo.setResult("平");
                userRecord.setWinning(userRecord.getWinning()+10);
                enemyRecord.setWinning(enemyRecord.getWinning()+10);
                pvpRecordService.saveOrUpdate(userRecord);
                pvpRecordService.saveOrUpdate(enemyRecord);
                pvpInfoService.saveOrUpdate(pvpInfo);
                return Result.getSuccess().setMsg("对战结束,双方平局");
            }
        }
        return Result.getSuccess().setMsg("对战结束");
    }
}
